/*
Rust 对可靠性的执着也延伸到了错误处理。错误对于软件来说是不可避免的，所以 Rust 有很多特性来处理出现错误的情况。
在很多情况下，Rust 要求你承认出错的可能性，并在编译代码之前就采取行动。这些要求使得程序更为健壮，
它们确保了你会在将代码部署到生产环境之前就发现错误并正确地处理它们！

Rust 将错误组合成两个主要类别：可恢复错误（recoverable）和 不可恢复错误（unrecoverable）。
可恢复错误通常代表向用户报告错误和重试操作是合理的情况，比如未找到文件。不可恢复错误通常是 bug 的同义词，
比如尝试访问超过数组结尾的位置。

大部分语言并不区分这两类错误，并采用类似异常这样方式统一处理他们。Rust 并没有异常，
但是，有可恢复错误 Result<T, E> ，和不可恢复(遇到错误时停止程序执行)错误 panic!。这一章会首先介绍 panic! 调用，
接着会讲到如何返回 Result<T, E>。此外，我们将探讨决定是尝试从错误中恢复还是停止执行时的注意事项。
*/

/// panic! 与不可恢复的错误
fn main() {
    panic!("crash and burn");
}

/// Result 与可恢复的错误
// T 和 E 是泛型类型参数； 现在你需要知道的就是 T 代表成功时返回的 Ok 成员中的数据的类型，
// 而 E 代表失败时返回的 Err 成员中的错误的类型。
fn main() {
    use std::fs::File;
    enum Result<T, E> {
        Ok(T),
        Err(E),
    }

    let f = File::open("hello.txt");

    let f = match f {
        Ok(file) => file,
        Err(error) => {
            panic!("Problem opening the file: {:?}", error)
        },
    };


    /// 匹配不同的错误
    let f = match f {
        Ok(file) => file,
        // 对不同的错误原因采取不同的行为
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Problem creating the file: {:?}", e),
            },
            other_error => panic!("Problem opening the file: {:?}", other_error),
        },
    };
}


use std::fs::File;
use std::io::{ErrorKind, Read};

fn main() {
    // 使用闭包改造上面的示例
    let f = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}


/// 失败时 panic 的简写：unwrap 和 expect
// 如果 Result 值是成员 Ok，unwrap 会返回 Ok 中的值。如果 Result 是成员 Err，unwrap 会为我们调用 panic!。
use std::fs::File;

// expect 与 unwrap 的使用方式一样：返回文件句柄或调用 panic! 宏。
// expect 在调用 panic! 时使用的错误信息将是我们传递给 expect 的参数，
// 而不像 unwrap 那样使用默认的 panic! 信息。
fn main() {
    // 如果不存在 hello.txt 文件， unwrap 会调用 panic!
    let f = File::open("hello.txt").unwrap();
    // 另一个类似于 unwrap 的方法它还允许我们选择 panic! 的错误信息：expect。
    // 使用 expect 而不是 unwrap 并提供一个好的错误信息可以表明你的意图并更易于追踪 panic 的根源
    let f = File::open("hello.txt").expect("Failed to open hello.txt");
}

/// 传播错误
// 当编写一个需要先调用一些可能会失败的操作的函数时，除了在这个函数中处理错误外，
// 还可以选择让调用者知道这个错误并决定该如何处理。这被称为 传播（propagating）错误，
/// 传播错误的简写：? 运算符
// Result 值之后的 ? 被定义为与 Result 值的 match 表达式有着完全相同的工作方式。
// 如果 Result 的值是 Ok，这个表达式将会返回 Ok 中的值而程序将继续执行。
// 如果值是 Err，Err 将作为整个函数的返回值，就好像使用了 return 关键字一样，这样错误值就被传播给了调用者。
fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    // 在 Result 值后面使用 ？ 符号
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}
/// 上面的代码等价于下面
fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("hello.txt");
    // 等价于 ？
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();

    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}


/// ? 运算符可被用于返回 Result 的函数
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("hello.txt")?;

    // 等价于上面的语句，这就意味着上面的语句会返回
    let f = File::open("hello.txt");
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    Ok(())
}